/*
 *  (C) Copyright 2010
 *  NVIDIA Corporation <www.nvidia.com>
 *
 * See file CREDITS for list of people who contributed to this
 * project.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of
 * the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 * MA 02111-1307 USA
 */

#include <common.h>
#include <nand.h>
#include <asm/io.h>
#include <asm/arch/sys_proto.h>
#include <asm/mach-types.h>
#include <asm/arch/nvcommon.h>
#include <asm/arch/nv_hardware_access.h>
#include <asm/arch/nv_drf.h>
#include <asm/arch/tegra2.h>
#include "sdmmc/nvboot_clocks_int.h"
#include "board.h"

///////////////////////////////////////////////////////////////////////////////
// PLL CONFIGURATION & PARAMETERS for different clock generators:
//-----------------------------------------------------------------------------
// Reference frequency     13.0MHz         19.2MHz         12.0MHz     26.0MHz
// ----------------------------------------------------------------------------
// PLLU_ENABLE_DLY_COUNT   02 (02h)        03 (03h)        02 (02h)    04 (04h)
// PLLU_STABLE_COUNT       51 (33h)        75 (4Bh)        47 (2Fh)   102 (66h)
// PLL_ACTIVE_DLY_COUNT    05 (05h)        06 (06h)        04 (04h)    09 (09h)
// XTAL_FREQ_COUNT        127 (7Fh)       187 (BBh)       118 (76h)   254 (FEh)
///////////////////////////////////////////////////////////////////////////////
static const UsbPllDelayParams s_UsbPllDelayParams[NvBootClocksOscFreq_Num] =
{
    //ENABLE_DLY,  STABLE_CNT,  ACTIVE_DLY,  XTAL_FREQ_CNT
    {0x02,         0x33,        0x05,        0x7F}, // For NvBootClocksOscFreq_13,
    {0x03,         0x4B,        0x06,        0xBB}, // For NvBootClocksOscFreq_19_2
    {0x02,         0x2F,        0x04,        0x76}, // For NvBootClocksOscFreq_12
    {0x04,         0x66,        0x09,        0xFE}  // For NvBootClocksOscFreq_26
};

///////////////////////////////////////////////////////////////////////////////////////////////////
//  PLLU configuration information (reference clock is osc/clk_m and PLLU-FOs are fixed at 12MHz/60MHz/480MHz).
//
//  reference frequency         13.0MHz         19.2MHz         12.0MHz         26.0MHz
//  ---------------------------------------------------------------------------------------
//      DIVN                    960 (3c0h)      200 (0c8h)      960 (3c0h)      960 (3c0h)
//      DIVM                    13 ( 0dh)        4 ( 04h)       12 ( 0ch)       26 ( 1ah)
// Filter frequency (MHz)       1               4.8             6           2
// CPCON                        1100b           0011b           1100b       1100b
// LFCON0                       0               0               0           0
///////////////////////////////////////////////////////////////////////////////
static const UsbPllClockParams s_UsbPllBaseInfo[NvBootClocksOscFreq_Num] =
{
    //DivN, DivM, DivP, CPCON,  LFCON
    {0x3C0, 0x0D, 0x00, 0xC,      0}, // For NvBootClocksOscFreq_13,
    {0x0C8, 0x04, 0x00, 0x3,      0}, // For NvBootClocksOscFreq_19_2
    {0x3C0, 0x0C, 0x00, 0xC,      0}, // For NvBootClocksOscFreq_12
    {0x3C0, 0x1A, 0x00, 0xC,      0}  // For NvBootClocksOscFreq_26
};

///////////////////////////////////////////////////////////////////////////////
// Debounce values IdDig, Avalid, Bvalid, VbusValid, VbusWakeUp, and SessEnd.
// Each of these signals have their own debouncer and for each of those one out
// of 2 debouncing times can be chosen (BIAS_DEBOUNCE_A or BIAS_DEBOUNCE_B.)
//
// The values of DEBOUNCE_A and DEBOUNCE_B are calculated as follows:
// 0xffff -> No debouncing at all
// <n> ms = <n> *1000 / (1/19.2MHz) / 4
// So to program a 1 ms debounce for BIAS_DEBOUNCE_A, we have:
// BIAS_DEBOUNCE_A[15:0] = 1000 * 19.2 / 4  = 4800 = 0x12c0
// We need to use only DebounceA for BOOTROM. We dont need the DebounceB
// values, so we can keep those to default.
///////////////////////////////////////////////////////////////////////////////
static const NvU32 s_UsbBiasDebounceATime[NvBootClocksOscFreq_Num] =
{
    /* Ten milli second delay for BIAS_DEBOUNCE_A */
    0x7EF4,  // For NvBootClocksOscFreq_13,
    0xBB80,  // For NvBootClocksOscFreq_19_2
    0x7530,  // For NvBootClocksOscFreq_12
    0xFDE8   // For NvBootClocksOscFreq_26
};

static const NvU32 s_UsbBiasTrkLengthTime[NvBootClocksOscFreq_Num] =
{
    /* 20 micro seconds delay after bias cell operation */
    5,  // For NvBootClocksOscFreq_13,
    7,  // For NvBootClocksOscFreq_19_2
    5,  // For NvBootClocksOscFreq_12
    9   // For NvBootClocksOscFreq_26
};

/* UTMIP Idle Wait Delay */
static const NvU8 s_UtmipIdleWaitDelay    = 17;
/* UTMIP Elastic limit */
static const NvU8 s_UtmipElasticLimit     = 16;
/* UTMIP High Speed Sync Start Delay */
static const NvU8 s_UtmipHsSyncStartDelay = 9;

void board_usb_init(void);
void board_spi_init(void);

/*
 * Routine: board_init
 * Description: Early hardware init.
 */
int board_init(void)
{
	DECLARE_GLOBAL_DATA_PTR;

	/* boot param addr */
	gd->bd->bi_boot_params = (NV_ADDRESS_MAP_SDRAM_BASE + 0x100);
	/* board id for Linux */
	gd->bd->bi_arch_number = LINUX_MACH_TYPE;

	board_spi_init();		/* do this early so UART mux is OK */
	board_usb_init();

	return 0;
}

/*
 * Routine: misc_init_r
 * Description: Configure board specific parts
 */
int misc_init_r(void)
{
	return 0;
}

/*
 * Routine: timer_init
 *
 * Description: init the timestamp and lastinc value
 *
 */
int timer_init(void)
{
    reset_timer();
    return 0;
}

/*
 * Routine: set_muxconf_regs
 * Description: Setting up the configuration Mux registers specific to the
 *		hardware. Many pins need to be moved from protect to primary
 *		mode.
 */
void set_muxconf_regs(void)
{
}

/***************************************************************************
 * Routines to be provided by corresponding device drivers.
 ***************************************************************************/
int board_nand_init(struct nand_chip *nand)
{
    return -1;
}

/***************************************************************************
 * Routines for UART initialization.
 ***************************************************************************/
void
NvBlAvpClockSetDivider(NvBool Enable, NvU32 Dividened, NvU32 Divisor)
{
    NvU32 val;

    if (Enable)
    {
        // Set up divider for SCLK.
        // SCLK is used for AVP, AHB, and APB.
        val = NV_DRF_DEF(CLK_RST_CONTROLLER, SUPER_SCLK_DIVIDER,
                         SUPER_SDIV_ENB, ENABLE)
              | NV_DRF_NUM(CLK_RST_CONTROLLER, SUPER_SCLK_DIVIDER,
                           SUPER_SDIV_DIVIDEND, Dividened - 1)
              | NV_DRF_NUM(CLK_RST_CONTROLLER, SUPER_SCLK_DIVIDER,
                           SUPER_SDIV_DIVISOR, Divisor - 1);
        NV_CLK_RST_WRITE(SUPER_SCLK_DIVIDER, val);
    }
    else
    {
        // Disable divider for SCLK.
        val = NV_DRF_DEF(CLK_RST_CONTROLLER, SUPER_SCLK_DIVIDER,
                         SUPER_SDIV_ENB, DISABLE);
        NV_CLK_RST_WRITE(SUPER_SCLK_DIVIDER, val);
    }
}

static char s_Hex2Char[] =
{
    '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
    'A', 'B', 'C', 'D', 'E', 'F'
};

static NV_INLINE NvU32
NvBlUartTxReadyA(void)
{
    NvU32 Reg;

    NV_UARTA_READ(LSR, Reg);
    return Reg & UART_LSR_0_THRE_FIELD;
}

static NV_INLINE NvU32
NvBlUartTxReadyD(void)
{
    NvU32 Reg;

    NV_UARTD_READ(LSR, Reg);
    return Reg & UART_LSR_0_THRE_FIELD;
}

NvU32
NvBlUartRxReadyA(void)
{
    NvU32 Reg;

    NV_UARTA_READ(LSR, Reg);
    return Reg & UART_LSR_0_RDR_FIELD;
}

NvU32
NvBlUartRxReadyD(void)
{
    NvU32 Reg;

    NV_UARTD_READ(LSR, Reg);
    return Reg & UART_LSR_0_RDR_FIELD;
}

static NV_INLINE void
NvBlUartTxA(NvU8 c)
{
    NV_UARTA_WRITE(THR_DLAB_0, c);
}

static NV_INLINE void
NvBlUartTxD(NvU8 c)
{
    NV_UARTD_WRITE(THR_DLAB_0, c);
}

NvU32
NvBlUartRxA(void)
{
    NvU32 Reg;

    NV_UARTA_READ(THR_DLAB_0, Reg);
    return Reg;
}

NvU32
NvBlUartRxD(void)
{
    NvU32 Reg;

    NV_UARTD_READ(THR_DLAB_0, Reg);
    return Reg;
}

int
NvBlUartPoll(void)
{
    if (NvBlUartRxReadyA())
        return NvBlUartRxA();

    if (NvBlUartRxReadyD())
        return NvBlUartRxD();

    return -1;
}

int NvBlUartWrite(const void *ptr);
void NvBlPrintf(const char *format, ...);
void uart_post(char c);

static void
NvBlPrintU4(NvU8 byte)
{
    uart_post(s_Hex2Char[byte & 0xF]);
}

void
NvBlPrintU8(NvU8 byte)
{
    NvBlPrintU4((byte >> 4) & 0xF);
    NvBlPrintU4((byte >> 0) & 0xF);
}

void
NvBlPrintU32(NvU32 word)
{
    NvBlPrintU8((word >> 24) & 0xFF);
    NvBlPrintU8((word >> 16) & 0xFF);
    NvBlPrintU8((word >>  8) & 0xFF);
    NvBlPrintU8((word >>  0) & 0xFF);
}

void
NvBlVprintf(const char *format, va_list ap)
{
    char msg[256];
    sprintf(msg, format, ap);
    NvBlUartWrite(msg);
}

void
NvBlPrintf(const char *format, ...)
{
    va_list ap;

    va_start(ap, format);
    NvBlVprintf(format, ap);
    va_end(ap);
}

void uart_post(char c)
{
#if defined(TEGRA2_TRACE)

#if (CONFIG_TEGRA2_ENABLE_UARTA)
    while (!NvBlUartTxReadyA())
        ;
    NvBlUartTxA(c);
#endif

#if (CONFIG_TEGRA2_ENABLE_UARTD)
    while (!NvBlUartTxReadyD())
        ;
    NvBlUartTxD(c);
#endif

#endif
}

void PostZz(void)
{
    uart_post(0x0d);
    uart_post(0x0a);
    uart_post('Z');
    uart_post('z');

    NvBlAvpStallUs(2000);
}

void PostYy(void)
{
    NvBlAvpStallMs(20);
    uart_post(0x0d);
    uart_post(0x0a);
    uart_post('Y');
    uart_post('y');
}

void PostXx(void)
{
    NvBlAvpStallMs(20);
    uart_post(0x0d);
    uart_post(0x0a);
    uart_post('X');
    uart_post('x');
    uart_post(0x0d);
    uart_post(0x0a);
}

int
NvBlUartWrite(const void *ptr)
{
    const NvU8 *p = ptr;

    while (*p)
    {
        if (*p == '\n') {
            uart_post(0x0D);
        }
        uart_post(*p);
        p++;
    }
    return 0;
}

void debug_trace(int i)
{
    uart_post(i+'a');
    uart_post('.');
    uart_post('.');
}

void UsbfResetController(NvU32 UsbBase)
{
	int RegVal = 0;

	if (UsbBase == NV_ADDRESS_MAP_USB_BASE)
	{
		/* Enable clock to the USB controller */
		RegVal= readl(NV_ADDRESS_MAP_CAR_BASE+CLK_RST_CONTROLLER_CLK_OUT_ENB_L_0);
		RegVal |= Bit22;
		writel(RegVal, NV_ADDRESS_MAP_CAR_BASE+CLK_RST_CONTROLLER_CLK_OUT_ENB_L_0);

		/* Reset the USB controller */
		RegVal= readl(NV_ADDRESS_MAP_CAR_BASE+CLK_RST_CONTROLLER_RST_DEVICES_L_0);
		RegVal |= Bit22;
		writel(RegVal, NV_ADDRESS_MAP_CAR_BASE+CLK_RST_CONTROLLER_RST_DEVICES_L_0);
		udelay(2);
		RegVal= readl(NV_ADDRESS_MAP_CAR_BASE+CLK_RST_CONTROLLER_RST_DEVICES_L_0);
		RegVal &= ~Bit22;
		writel(RegVal, NV_ADDRESS_MAP_CAR_BASE+CLK_RST_CONTROLLER_RST_DEVICES_L_0);
		udelay(2);

		/* Set USB1_NO_LEGACY_MODE to 1 */
		RegVal= readl(UsbBase+USB1_LEGACY_CTRL);
		RegVal |= Bit0;
		writel(RegVal, UsbBase+USB1_LEGACY_CTRL);
	}
	else if(UsbBase == NV_ADDRESS_MAP_USB3_BASE)
	{
		/* Enable clock to the USB3 controller */
		RegVal= readl(NV_ADDRESS_MAP_CAR_BASE+CLK_RST_CONTROLLER_CLK_OUT_ENB_L_0+4);
		RegVal |= Bit27;
		writel(RegVal, NV_ADDRESS_MAP_CAR_BASE+CLK_RST_CONTROLLER_CLK_OUT_ENB_L_0+4);

		/* Reset USB3 controller */
		RegVal= readl(NV_ADDRESS_MAP_CAR_BASE+CLK_RST_CONTROLLER_RST_DEVICES_L_0+4);
		if (RegVal & Bit27)
		{
			writel(RegVal, NV_ADDRESS_MAP_CAR_BASE+CLK_RST_CONTROLLER_RST_DEVICES_L_0+4);
			udelay(2);
			RegVal= readl(NV_ADDRESS_MAP_CAR_BASE+CLK_RST_CONTROLLER_RST_DEVICES_L_0+4);
			RegVal &= ~Bit27;
			writel(RegVal, NV_ADDRESS_MAP_CAR_BASE+CLK_RST_CONTROLLER_RST_DEVICES_L_0+4);
			udelay(2);
		}
	}

	/*
	 * Assert UTMIP_RESET in USB1/3_IF_USB_SUSP_CTRL register to put
	 * UTMIP1/3 in reset.
	 */
	RegVal= readl(UsbBase+USB_SUSP_CTRL);
	RegVal |= Bit11;
	writel(RegVal, UsbBase+USB_SUSP_CTRL);

	if(UsbBase == NV_ADDRESS_MAP_USB3_BASE)
	{
		/*
		 * Set USB3 to use UTMIP PHY by setting
		 * USB3_IF_USB_SUSP_CTRL.UTMIP_PHY_ENB  register to 1.
		 */
        	RegVal = readl(UsbBase+USB_SUSP_CTRL);
		RegVal |= Bit12;
		writel(RegVal, UsbBase+USB_SUSP_CTRL);
	}

}

void board_usb_init(void)
{
	int RegVal;
	int i;
	NvU32 PlluStableTime =0;
	NvBootClocksOscFreq OscFreq;
	int UsbBase;
	int loop_count;
	int PhyClkValid;

	/* Get the Oscillator frequency */
	OscFreq = NvBootClocksGetOscFreq();

	/* Enable PLL U for USB */
	NvBootClocksStartPll(NvBootClocksPllId_PllU,
                         s_UsbPllBaseInfo[OscFreq].M,
                         s_UsbPllBaseInfo[OscFreq].N,
                         s_UsbPllBaseInfo[OscFreq].P,
                         s_UsbPllBaseInfo[OscFreq].CPCON,
                         s_UsbPllBaseInfo[OscFreq].LFCON,
                         &PlluStableTime);

	/* Initialize USB1 & USB3 controllers */
	for (i = 0; i < 2; i++)
	{
        /* Select the correct base address for the ith controller. */
        UsbBase = (i) ? NV_ADDRESS_MAP_USB3_BASE : NV_ADDRESS_MAP_USB_BASE;

        /* Reset the usb controller. */
        UsbfResetController(UsbBase);

        /* Stop crystal clock by setting UTMIP_PHY_XTAL_CLOCKEN low */
	RegVal= readl(UsbBase+UTMIP_MISC_CFG1);
	RegVal &= ~Bit30;
	writel(RegVal, UsbBase+UTMIP_MISC_CFG1);

        /* Follow the crystal clock disable by >100ns delay. */
	udelay(1);

        /*
         * To Use the A Session Valid for cable detection logic,
         * VBUS_WAKEUP mux must be switched to actually use a_sess_vld
         * threshold.  This is done by setting VBUS_SENSE_CTL bit in
         * USB_LEGACY_CTRL register.
         */
        if (UsbBase == NV_ADDRESS_MAP_USB_BASE)
        {
		RegVal= readl(UsbBase+USB1_LEGACY_CTRL);
		RegVal |= (Bit2+Bit1);
		writel(RegVal, UsbBase+USB1_LEGACY_CTRL);
        }

	/* PLL Delay CONFIGURATION settings
	 * The following parameters control the bring up of the plls:
	 */
	RegVal= readl(UsbBase+UTMIP_MISC_CFG1);
	RegVal &= 0xFFFC003F;
	RegVal |= (s_UsbPllDelayParams[OscFreq].StableCount << 6);

	RegVal &= 0xFF83FFFF;
	RegVal |= (s_UsbPllDelayParams[OscFreq].ActiveDelayCount << 18);
	writel(RegVal, UsbBase+UTMIP_MISC_CFG1);

	/* Set PLL enable delay count and Crystal frequency count */
	RegVal= readl(UsbBase+UTMIP_PLL_CFG1);
	RegVal &= 0x08FFFFFF;
	RegVal |= (s_UsbPllDelayParams[OscFreq].EnableDelayCount <<27);

	RegVal &= 0xFFFFF000;
	RegVal |= (s_UsbPllDelayParams[OscFreq].XtalFreqCount);
	writel(RegVal, UsbBase+UTMIP_PLL_CFG1);

	/* Setting the tracking length time. */
	RegVal= readl(UsbBase+UTMIP_BIAS_CFG1);
	RegVal &= 0xFFFFFF07;
	RegVal |= (s_UsbBiasTrkLengthTime[OscFreq] <<3);
	writel(RegVal, UsbBase+UTMIP_BIAS_CFG1);

	/* Program Debounce time for VBUS to become valid. */
	RegVal= readl(UsbBase+UTMIP_DEBOUNCE_CFG0);
	RegVal &= 0xFFFF0000;
	RegVal |= s_UsbBiasDebounceATime[OscFreq];
	writel(RegVal, UsbBase+UTMIP_DEBOUNCE_CFG0);

	/* Set UTMIP_FS_PREAMBLE_J to 1 */
	RegVal= readl(UsbBase+UTMIP_TX_CFG0);
	RegVal |= Bit19;
	writel(RegVal, UsbBase+UTMIP_TX_CFG0);

	/* Disable Batery charge enabling bit set to '1' for disable */
	RegVal= readl(UsbBase+UTMIP_BAT_CHRG_CFG0);
	RegVal |= Bit0;
	writel(RegVal, UsbBase+UTMIP_BAT_CHRG_CFG0);

	/* Set UTMIP_XCVR_LSBIAS_SEL to 0 */
	RegVal= readl(UsbBase+UTMIP_XCVR_CFG0);
	RegVal &= ~Bit21;
	writel(RegVal, UsbBase+UTMIP_XCVR_CFG0);

	/* Set bit 3 of UTMIP_SPARE_CFG0 to 1 */
	RegVal= readl(UsbBase+UTMIP_SPARE_CFG0);
	RegVal |= Bit3;
	writel(RegVal, UsbBase+UTMIP_SPARE_CFG0);

        /* Configure the UTMIP_IDLE_WAIT and UTMIP_ELASTIC_LIMIT
         * Setting these fields, together with default values of the other
         * fields, results in programming the registers below as follows:
         *         UTMIP_HSRX_CFG0 = 0x9168c000
         *         UTMIP_HSRX_CFG1 = 0x13
         */

	/* Set PLL enable delay count and Crystal frequency count */
	RegVal= readl(UsbBase+UTMIP_HSRX_CFG0);
	RegVal &= 0xFFF07FFF;
	RegVal |= s_UtmipIdleWaitDelay << 15;

	RegVal &= 0xFFFF83FF;
	RegVal |= s_UtmipElasticLimit << 10;
	writel(RegVal, UsbBase+UTMIP_HSRX_CFG0);

	/* Configure the UTMIP_HS_SYNC_START_DLY */
	RegVal= readl(UsbBase+UTMIP_HSRX_CFG1);
	RegVal &= 0xFFFFFFC1;
	RegVal |= s_UtmipHsSyncStartDelay << 1;
	writel(RegVal, UsbBase+UTMIP_HSRX_CFG1);

	/* Preceed  the crystal clock disable by >100ns delay. */
        udelay(1);

	/* Resuscitate  crystal clock by setting UTMIP_PHY_XTAL_CLOCKEN */
	RegVal= readl(UsbBase+UTMIP_MISC_CFG1);
	RegVal |= Bit30;
	writel(RegVal, UsbBase+UTMIP_MISC_CFG1);
	}

	/* Finished the per-controller init. */

	/* De-assert UTMIP_RESET in USB3_IF_USB_SUSP_CTRL register
	 * to bring out of reset.
	 */
	RegVal= readl(UsbBase+USB_SUSP_CTRL);
	RegVal &= ~Bit11;
	writel(RegVal, UsbBase+USB_SUSP_CTRL);

	loop_count = 100000;
	while (loop_count)
	{
	/* Wait for the phy clock to become valid in 100 ms */
	PhyClkValid = readl(UsbBase+USB_SUSP_CTRL) & Bit7;
	if (PhyClkValid)
		break;
	udelay(1);
	loop_count--;
	}

	/* Disable by writing IC_ENB1  in USB2_CONTROLLER_2_USB2D_ICUSB_CTRL_0. */
	RegVal= readl(NV_ADDRESS_MAP_USB3_BASE+ICUSB_CTRL);
	RegVal &= ~Bit3;
	writel(RegVal, NV_ADDRESS_MAP_USB3_BASE+ICUSB_CTRL);

	/* Setting STS field to 0 and PTS field to 0 */
	RegVal= readl(NV_ADDRESS_MAP_USB3_BASE+PORTSC1);
	RegVal &= ~(Bit31+Bit30+Bit29);
	writel(RegVal, NV_ADDRESS_MAP_USB3_BASE+PORTSC1);

	/* Deassert power down state */
	RegVal= readl(UsbBase+UTMIP_XCVR_CFG0);
	RegVal &= ~(Bit18+Bit16+Bit14);
	writel(RegVal, UsbBase+UTMIP_XCVR_CFG0);

	RegVal= readl(UsbBase+UTMIP_XCVR_CFG1);
	RegVal &= ~(Bit4+Bit2+Bit0);
	writel(RegVal, UsbBase+UTMIP_XCVR_CFG1);
#if 0
	/* Hold the reset for UTMIP3. */
	RegVal= readl(UsbBase+USB_SUSP_CTRL);
	RegVal |= Bit11;
	writel(RegVal, UsbBase+USB_SUSP_CTRL);

	loop_count = 100000;
	while (loop_count)
	{
        //Wait for the phy clock to become 0 in 100 ms
        PhyClkValid = readl(UsbBase+USB_SUSP_CTRL) & Bit7;
	if (!PhyClkValid)
		break;
        udelay(1);
        loop_count--;
	}
#endif
}
